【STM32】CubeMX+HAL库之 硬件IIC+DMA控制OLED(兼容SSD1306&SH1106驱动)

您所在的位置:网站首页 stm32 oled驱动不了 【STM32】CubeMX+HAL库之 硬件IIC+DMA控制OLED(兼容SSD1306&SH1106驱动)

【STM32】CubeMX+HAL库之 硬件IIC+DMA控制OLED(兼容SSD1306&SH1106驱动)

2024-06-01 00:17| 来源: 网络整理| 查看: 265

【STM32】CubeMX+HAL库之 硬件IIC+DMA控制1.3寸OLED 前言

目前网上大多数驱动OLED屏都采用软件IIC,因为HAL库的升级使得硬件IIC的稳定性得到了保障,所以想采用硬件IIC+DMA的方式控制OLED,在最初的时候,换用硬件IIC极其的方便,换到HAL库的接口函数就好了,但打开DMA就炸,仔细想了一下,知道原因 (由于DMA为非阻塞模式,反复调用函数但硬件并未准备好,所以并未执行) 经过拼包DMA发送,CPU的负担大幅降低,显示效果良好。 下文从CubeMX的配置开始说起,并介绍两个DMA发送函数以及相应的发送完成回调函数,最后附上代码实体。(根据中景园历程修改的DMA发送版本)。

所用工具:

开发板:STM32G474RESTM32CubeMXIDE: Keil-MDK STM32CubeMX IIC配置 时钟配置 (如不了解点击下面链接) 【STM32】CubeMX+HAL库之时钟. 在这里插入图片描述 在这里插入图片描述 2.IIC配置 开启需要的通道,并选择合适的引脚。 在这里插入图片描述 由于使用的 STM32G474 IIC有恶魔般的 Fast Mode Plus版本所以选用这个,使得性能拉满。 当然使用其他模式也都可以。其他配置都不需要,默认就好。 3.开启DMA I2Cx_TX 的通道 在这里插入图片描述 4.开启相关中断 在这里插入图片描述 5.生成代码 点击右上角 GENERATE CODE HAL库函数

主要的DMA发送函数有两个

HAL_I2C_Mem_Write_DMA(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint16_t MemAddress,uint16_t MemAddSize, uint8_t *pData, uint16_t Size); HAL_I2C_Master_Transmit_DMA(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData,uint16_t Size);

两函数具有较为明显的区别,就是在HAL_I2C_Mem_Write_DMA函数中有MemAddress 这一参数,MemAddress 代表了传输到目标机的内存地址,而使用 HAL_I2C_Master_Transmit_DMA函数需要在pData的第一位加入目标内存地址。 这两个函数同时有着各自的完成回调函数分别是 HAL_I2C_MemTxCpltCallback HAL_I2C_MasterTxCpltCallback

因为对OLED操作需要先发送cmd发送坐标位置,在发送数据,这种方式刷新一次屏幕需要发送多次信息,所以为了保证信息都可以被接收,使用完成回调函数,发送下次的数据。

代码示例 解决方案

1.建立两个缓冲Buffer 建立一个缓冲Buffer uint8_t OLED_GRAMbuf[8][128]; 存储整个屏幕需要现实的信息。 再建立cmd地址Buffer uint8_t OLED_CMDbuf[8][4] ; 存储地址信息。 2.在整屏刷新时开启第一次DMA发送cmd函数,进入完成传输后进入完成回调函数,发送第二次的数据,待发送完成后进入下次的回调函数 ~~~~直至完成所有的传输。

/** * @brief HAL_I2C_Master_Transmit_DMA(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size)完成回调函数 * 保证DMA传输完成后,开启下次DMA */ void HAL_I2C_MasterTxCpltCallback(I2C_HandleTypeDef *hi2c) { if(BufFinshFlag) { HAL_I2C_Mem_Write_DMA(&hi2c1,0x78,0x40,I2C_MEMADD_SIZE_8BIT,OLED_GRAMbuf[CountFlag],128); } } /** * @brief HAL_I2C_Mem_Write_DMA(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint16_t MemAddress, uint16_t MemAddSize, uint8_t *pData, uint16_t Size)完成回调函数 * 保证DMA传输完成后,开启下次DMA */ void HAL_I2C_MemTxCpltCallback(I2C_HandleTypeDef *hi2c) { if(CountFlag == 7) { BufFinshFlag = 0; CountFlag = 0; } if(BufFinshFlag) { CountFlag ++; HAL_I2C_Master_Transmit_DMA(&hi2c1,0x78,OLED_CMDbuf[CountFlag],4); } } /** * @brief 发送数据到OLED的GRAM * @param[in] none * @retval none */ void OLED_refresh_gram(void) { uint8_t i; uint16_t j; if(BufFinshFlag == 0) { for(i = 0; i OLED_GRAMbuf[i][j] = OLED_GRAM[j][i]; //OLED_GRAM[128][8] } } BufFinshFlag = 1; HAL_I2C_Master_Transmit_DMA(&hi2c1,0x78,OLED_CMDbuf[0],4); } }

详细看下面的整文件分享

代码部分

main.c

OLED_operate_gram(PEN_CLEAR);//清缓存 OLED_show_string(0,5,(uint8_t*)"show string"); OLED_show_floatnum(2, 12, num, 0); OLED_refresh_gram();//全局刷新

oled.c

#include "OLED.h" #include "oledfont.h" #include "main.h" #include #include #include extern I2C_HandleTypeDef hi2c1; uint8_t OLED_GRAM[128][8]; uint8_t OLED_GRAMbuf[8][128]; uint8_t OLED_CMDbuf[8][4] = {0}; uint8_t I2C1_MemTxFinshFlag = 1; uint8_t CountFlag = 0; uint8_t BufFinshFlag = 0; /** * @brief HAL_I2C_Master_Transmit_DMA(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size)完成回调函数 * 保证DMA传输完成后,开启下次DMA */ void HAL_I2C_MasterTxCpltCallback(I2C_HandleTypeDef *hi2c) { if(BufFinshFlag) { HAL_I2C_Mem_Write_DMA(&hi2c1,0x78,0x40,I2C_MEMADD_SIZE_8BIT,OLED_GRAMbuf[CountFlag],128); } } /** * @brief HAL_I2C_Mem_Write_DMA(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint16_t MemAddress, uint16_t MemAddSize, uint8_t *pData, uint16_t Size)完成回调函数 * 保证DMA传输完成后,开启下次DMA */ void HAL_I2C_MemTxCpltCallback(I2C_HandleTypeDef *hi2c) { if(CountFlag == 7) { BufFinshFlag = 0; CountFlag = 0; } if(BufFinshFlag) { CountFlag ++; HAL_I2C_Master_Transmit_DMA(&hi2c1,0x78,OLED_CMDbuf[CountFlag],4); } } uint32_t oled_pow(uint8_t m, uint8_t n) { uint32_t result = 1; while (n--) result *= m; return result; } uint8_t check_num_len(uint32_t num) { uint32_t tmp; uint8_t i; for(i = 1; i return i; } } return 0; } void OLED_show_num(uint8_t x, uint8_t y, uint32_t num, uint8_t mode, uint8_t len) { uint8_t t, temp; uint8_t enshow = 0; for (t = 0; t if (temp == 0) { if (mode == 0) OLED_show_char(x, y + t, ' '); else OLED_show_char(x, y + t, '0'); continue; } else enshow = 1; } OLED_show_char(x, y + t, temp + '0'); } } void OLED_show_floatnum(uint8_t x, uint8_t y, float num, uint8_t mode) { int32_t m, n; float R; uint8_t chartemp[6], i; R = num; m = R / 1; n = (R - m) * 1000; if(n HAL_I2C_Mem_Write_DMA(&hi2c1, OLED_I2C_ADDRESS, 0x00, I2C_MEMADD_SIZE_8BIT, OLED_Init_CMD, 29); } /** * @brief operate the graphic ram(size: 128*8 char) * @param[in] pen: the type of operate. PEN_CLEAR: set ram to 0x00 PEN_WRITE: set ram to 0xff PEN_INVERSION: bit inversion * @retval none */ /** * @brief 操作GRAM内存(128*8char数组) * @param[in] pen: 操作类型. PEN_CLEAR: 设置为0x00 PEN_WRITE: 设置为0xff * @retval none */ void OLED_operate_gram(pen_typedef pen) { if (pen == PEN_WRITE) { memset(OLED_GRAM,0xff,sizeof(OLED_GRAM)); } else if(pen == PEN_CLEAR) { memset(OLED_GRAM,0x00,sizeof(OLED_GRAM)); } } /** * @brief cursor set to (x,y) point * @param[in] x:X-axis, from 0 to 127 * @param[in] y:Y-axis, from 0 to 7 * @retval none */ /** * @brief 设置光标起点(x,y) * @param[in] x:x轴, 从 0 到 127 * @param[in] y:y轴, 从 0 到 7 * @retval none */ void OLED_set_pos(uint8_t x, uint8_t y) { OLED_CMDbuf[y][0] = 0x00; OLED_CMDbuf[y][1] = 0xb0 + y; OLED_CMDbuf[y][2] = 0x10; OLED_CMDbuf[y][3] = 0x00; } /** * @brief draw one bit of graphic raw, operate one point of screan(128*64) * @param[in] x: x-axis, [0, X_WIDTH-1] * @param[in] y: y-axis, [0, Y_WIDTH-1] * @param[in] pen: type of operation, PEN_CLEAR: set (x,y) to 0 PEN_WRITE: set (x,y) to 1 PEN_INVERSION: (x,y) value inversion * @retval none */ /** * @brief 操作GRAM中的一个位,相当于操作屏幕的一个点 * @param[in] x:x轴, [0,X_WIDTH-1] * @param[in] y:y轴, [0,Y_WIDTH-1] * @param[in] pen: 操作类型, PEN_CLEAR: 设置 (x,y) 点为 0 PEN_WRITE: 设置 (x,y) 点为 1 PEN_INVERSION: (x,y) 值反转 * @retval none */ void OLED_draw_point(int8_t x, int8_t y, pen_typedef pen) { uint8_t page = 0, row = 0; /* check the corrdinate */ if ((x (X_WIDTH - 1)) || (y (Y_WIDTH - 1))) { return; } page = y / 8; row = y % 8; if (pen == PEN_WRITE) { OLED_GRAM[x][page] |= 1 OLED_GRAM[x][page] &= ~(1 (x1 (y1 k = ((float)(y2 - y1)) / (x2 - x1); b = (float)y1 - k * x1; (x1 uint8_t x = col * 6; uint8_t y = row * 12; uint8_t temp, t, t1; uint8_t y0 = y; chr = chr - ' '; for (t = 0; t if (temp&0x80) OLED_draw_point(x, y, PEN_WRITE); else OLED_draw_point(x, y, PEN_CLEAR); temp uint8_t n =0; while (chr[n] != '\0') { OLED_show_char(row, col, chr[n]); col++; if (col > 20) { col = 0; row += 1; } n++; } } /** * @brief formatted output in oled 128*64 * @param[in] row: row of character string begin, 0 0}; static va_list ap; uint8_t remain_size = 0; if ((row > 4) || (col > 20) ) { return; } va_start(ap, fmt); vsprintf((char *)LCD_BUF, fmt, ap); va_end(ap); remain_size = 21 - col; LCD_BUF[remain_size] = '\0'; OLED_show_string(row, col, LCD_BUF); } /** * @brief send the data of gram to oled sreen * @param[in] none * @retval none */ /** * @brief 发送数据到OLED的GRAM * @param[in] none * @retval none */ void OLED_refresh_gram(void) { uint8_t i; uint16_t j; if(BufFinshFlag == 0) { for(i = 0; i OLED_GRAMbuf[i][j] = OLED_GRAM[j][i]; //OLED_GRAM[128][8] } } BufFinshFlag = 1; HAL_I2C_Master_Transmit_DMA(&hi2c1,0x78,OLED_CMDbuf[0],4); } } /** * @brief show the logo of RoboMaster * @param[in] none * @retval none */ /** * @brief 显示RM的LOGO * @param[in] none * @retval none */ void OLED_LOGO(void) { uint8_t temp_char = 0; uint8_t x = 0, y = 0; uint8_t i = 0; OLED_operate_gram(PEN_CLEAR); for(; y temp_char = LOGO_BMP[x][y/8]; for(i = 0; i OLED_draw_point(x, y + i,PEN_WRITE); } else { OLED_draw_point(x,y + i,PEN_CLEAR); } temp_char {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*" ",0*/ {0x00,0x00,0x00,0x00,0x3F,0x40,0x00,0x00,0x00,0x00,0x00,0x00},/*"!",1*/ {0x00,0x00,0x30,0x00,0x40,0x00,0x30,0x00,0x40,0x00,0x00,0x00},/*""",2*/ {0x09,0x00,0x0B,0xC0,0x3D,0x00,0x0B,0xC0,0x3D,0x00,0x09,0x00},/*"#",3*/ {0x18,0xC0,0x24,0x40,0x7F,0xE0,0x22,0x40,0x31,0x80,0x00,0x00},/*"$",4*/ {0x18,0x00,0x24,0xC0,0x1B,0x00,0x0D,0x80,0x32,0x40,0x01,0x80},/*"%",5*/ {0x03,0x80,0x1C,0x40,0x27,0x40,0x1C,0x80,0x07,0x40,0x00,0x40},/*"&",6*/ {0x10,0x00,0x60,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*"'",7*/ {0x00,0x00,0x00,0x00,0x00,0x00,0x1F,0x80,0x20,0x40,0x40,0x20},/*"(",8*/ {0x00,0x00,0x40,0x20,0x20,0x40,0x1F,0x80,0x00,0x00,0x00,0x00},/*")",9*/ {0x09,0x00,0x06,0x00,0x1F,0x80,0x06,0x00,0x09,0x00,0x00,0x00},/*"*",10*/ {0x04,0x00,0x04,0x00,0x3F,0x80,0x04,0x00,0x04,0x00,0x00,0x00},/*"+",11*/ {0x00,0x10,0x00,0x60,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*",",12*/ {0x04,0x00,0x04,0x00,0x04,0x00,0x04,0x00,0x04,0x00,0x00,0x00},/*"-",13*/ {0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*".",14*/ {0x00,0x20,0x01,0xC0,0x06,0x00,0x38,0x00,0x40,0x00,0x00,0x00},/*"/",15*/ {0x1F,0x80,0x20,0x40,0x20,0x40,0x20,0x40,0x1F,0x80,0x00,0x00},/*"0",16*/ {0x00,0x00,0x10,0x40,0x3F,0xC0,0x00,0x40,0x00,0x00,0x00,0x00},/*"1",17*/ {0x18,0xC0,0x21,0x40,0x22,0x40,0x24,0x40,0x18,0x40,0x00,0x00},/*"2",18*/ {0x10,0x80,0x20,0x40,0x24,0x40,0x24,0x40,0x1B,0x80,0x00,0x00},/*"3",19*/ {0x02,0x00,0x0D,0x00,0x11,0x00,0x3F,0xC0,0x01,0x40,0x00,0x00},/*"4",20*/ {0x3C,0x80,0x24,0x40,0x24,0x40,0x24,0x40,0x23,0x80,0x00,0x00},/*"5",21*/ {0x1F,0x80,0x24,0x40,0x24,0x40,0x34,0x40,0x03,0x80,0x00,0x00},/*"6",22*/ {0x30,0x00,0x20,0x00,0x27,0xC0,0x38,0x00,0x20,0x00,0x00,0x00},/*"7",23*/ {0x1B,0x80,0x24,0x40,0x24,0x40,0x24,0x40,0x1B,0x80,0x00,0x00},/*"8",24*/ {0x1C,0x00,0x22,0xC0,0x22,0x40,0x22,0x40,0x1F,0x80,0x00,0x00},/*"9",25*/ {0x00,0x00,0x00,0x00,0x08,0x40,0x00,0x00,0x00,0x00,0x00,0x00},/*":",26*/ {0x00,0x00,0x00,0x00,0x04,0x60,0x00,0x00,0x00,0x00,0x00,0x00},/*";",27*/ {0x00,0x00,0x04,0x00,0x0A,0x00,0x11,0x00,0x20,0x80,0x40,0x40},/*"


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3